package main

import (
	"encoding/json"
	"fmt"
	_"io"
	"math"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
)

const (
	PORT       = ":22330"
	UPLOAD_DIR = "./uploads"
	DB_FILE    = "db.json"
)

var (
	db   *Database
	lock sync.RWMutex
)

type Database struct {
	Apps       map[uint]App       `json:"apps"`
	Comments   map[uint][]Comment `json:"comments"`
	Ratings    map[uint][]Rating  `json:"ratings"`
	LastAppID  uint               `json:"last_app_id"`
	Categories []string           `json:"categories"`
}

type App struct {
	ID          uint      `json:"id"`
	Name        string    `json:"name"`
	PackageName string    `json:"package_name"`
	Version     string    `json:"version"`
	Category    string    `json:"category"`
	Description string    `json:"description"`
	Downloads   int       `json:"downloads"`
	IconURL     string    `json:"icon_url"`
	PackageURL  string    `json:"package_url"`
	CreatedAt   time.Time `json:"created_at"`
}

type Comment struct {
	ID        uint      `json:"id"`
	AppID     uint      `json:"app_id"`
	UserID    uint      `json:"user_id"`
	Content   string    `json:"content"`
	CreatedAt time.Time `json:"created_at"`
}

type Rating struct {
	AppID     uint      `json:"app_id"`
	UserID    uint      `json:"user_id"`
	Score     int       `json:"score"`
	CreatedAt time.Time `json:"created_at"`
}

func main() {
	// 初始化数据库
	if err := initDB(); err != nil {
		panic(err)
	}

	// 确保上传目录存在
	os.MkdirAll(UPLOAD_DIR, os.ModePerm)

	r := gin.Default()
	r.Static("/uploads", UPLOAD_DIR)

	// 应用相关接口
	r.GET("/api/apps", getApps)
	r.POST("/api/apps", createApp)
	r.GET("/api/apps/:id", getAppDetail)
	r.POST("/api/apps/upload", uploadAppPackage)
	r.GET("/api/categories", getCategories)

	// 评论评分接口
	r.POST("/api/comments", addComment)
	r.GET("/api/comments", getComments)
	r.POST("/api/ratings", addRating)

	r.Run(PORT)
}

// 初始化数据库
func initDB() error {
	if _, err := os.Stat(DB_FILE); os.IsNotExist(err) {
		db = &Database{
			Apps:       make(map[uint]App),
			Comments:   make(map[uint][]Comment),
			Ratings:    make(map[uint][]Rating),
			Categories: []string{"游戏", "工具", "社交", "教育"},
		}
		return saveDB()
	}

	data, err := os.ReadFile(DB_FILE)
	if err != nil {
		return err
	}
	return json.Unmarshal(data, &db)
}

// 保存数据库
func saveDB() error {
	lock.Lock()
	defer lock.Unlock()
	data, err := json.MarshalIndent(db, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(DB_FILE, data, 0644)
}

// 获取应用列表
func getApps(c *gin.Context) {
	lock.RLock()
	defer lock.RUnlock()

	category := c.Query("category")
	apps := make([]App, 0)

	for _, app := range db.Apps {
		if category == "" || app.Category == category {
			apps = append(apps, app)
		}
	}

	c.JSON(http.StatusOK, gin.H{"data": apps})
}

// 创建应用
func createApp(c *gin.Context) {
	var app App
	if err := c.ShouldBindJSON(&app); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	lock.Lock()
	defer lock.Unlock()

	db.LastAppID++
	app.ID = db.LastAppID
	app.CreatedAt = time.Now()
	db.Apps[app.ID] = app

	if err := saveDB(); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, app)
}

// 获取应用详情
func getAppDetail(c *gin.Context) {
	id, err := parseAppID(c)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid app id"})
		return
	}

	lock.RLock()
	defer lock.RUnlock()

	app, exists := db.Apps[id]
	if !exists {
		c.JSON(http.StatusNotFound, gin.H{"error": "app not found"})
		return
	}

	// 计算平均评分
	var totalScore float64
	ratings := db.Ratings[id]
	for _, r := range ratings {
		totalScore += float64(r.Score)
	}
	avgScore := math.Round(totalScore/float64(len(ratings))*10) / 10

	response := gin.H{
		"app":          app,
		"comments":     db.Comments[id],
		"rating_count": len(ratings),
		"avg_score":    avgScore,
	}

	c.JSON(http.StatusOK, response)
}

// 上传应用包
func uploadAppPackage(c *gin.Context) {
	file, err := c.FormFile("package")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	id, err := parseAppID(c)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid app id"})
		return
	}

	// 生成唯一文件名
	ext := filepath.Ext(file.Filename)
	newName := fmt.Sprintf("app_%d_%d%s", id, time.Now().UnixNano(), ext)
	dst := filepath.Join(UPLOAD_DIR, newName)

	// 保存文件
	if err := c.SaveUploadedFile(file, dst); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// 更新应用记录
	lock.Lock()
	defer lock.Unlock()
	app := db.Apps[id]
	app.PackageURL = "/uploads/" + newName
	db.Apps[id] = app

	if err := saveDB(); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"package_url": app.PackageURL})
}

// 添加评论
func addComment(c *gin.Context) {
	var comment Comment
	if err := c.ShouldBindJSON(&comment); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	lock.Lock()
	defer lock.Unlock()

	comment.ID = uint(len(db.Comments[comment.AppID]) + 1)
	comment.CreatedAt = time.Now()
	db.Comments[comment.AppID] = append(db.Comments[comment.AppID], comment)

	if err := saveDB(); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, comment)
}

// 获取评论
func getComments(c *gin.Context) {
	appID, _ := strconv.ParseUint(c.Query("app_id"), 10, 32)
	lock.RLock()
	defer lock.RUnlock()
	c.JSON(http.StatusOK, db.Comments[uint(appID)])
}

// 添加评分
func addRating(c *gin.Context) {
	var rating Rating
	if err := c.ShouldBindJSON(&rating); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if rating.Score < 1 || rating.Score > 5 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "score must between 1-5"})
		return
	}

	lock.Lock()
	defer lock.Unlock()

	rating.CreatedAt = time.Now()
	db.Ratings[rating.AppID] = append(db.Ratings[rating.AppID], rating)

	if err := saveDB(); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, rating)
}

// 获取分类
func getCategories(c *gin.Context) {
	lock.RLock()
	defer lock.RUnlock()
	c.JSON(http.StatusOK, db.Categories)
}

func parseAppID(c *gin.Context) (uint, error) {
	idStr := c.Param("id") // 或者从请求参数获取
	if idStr == "" {
		idStr = c.Query("app_id")
	}
	id, err := strconv.ParseUint(idStr, 10, 32)
	return uint(id), err
}
